﻿
using System.Collections.Generic;
using UnityEngine;

namespace Game.Global.Buffer
{
    /// <summary>
    /// 提供缓存机制的Object buffer，在需要时获得缓存中的对应的obj，如果不足则诞生新物体；用完要记得归还哦！
    /// </summary>
    public class GameObjBuffer
    {
        /// <summary>
        /// 全局共享的buffer
        /// </summary>
        static Dictionary<GameObject, GameObjBuffer> g_sharedBuffer = null;

        Stack<GameObject> m_bufferList;
        GameObject m_objPrefab;

        /// <summary>
        /// 理论上的大小（包含还在缓冲区的，和已经分配出去的）
        /// </summary>
        int m_historyCount;
        /// <summary>
        /// 获得全局共享（可能多场景下）的某个物体对应的buffer，需要慎用
        /// </summary>
        /// <returns>The global buffer.</returns>
        /// <param name="prefab">Prefab.</param>
        public static GameObjBuffer GetGlobalBuffer(GameObject prefab, int defaultBufferCount = 3)
        {
            //场景中公用的一个buffer：
            if (g_sharedBuffer == null)
            {
                g_sharedBuffer = new Dictionary<GameObject, GameObjBuffer>();
            }

            if (!g_sharedBuffer.ContainsKey(prefab)
               || g_sharedBuffer[prefab] == null)
            {
                g_sharedBuffer[prefab] = new GameObjBuffer(prefab, defaultBufferCount);
            }
            else
            {
                //TODO: 存在buffer的情况下，还需防范场景被destroy过，则有可能buffer中的内容也被destroy掉了
            }


            return g_sharedBuffer[prefab];
        }

        /// <summary>
        /// 切换场景时使用，清全局buff
        /// </summary>
        public static void ClearGlobalBuffer()
        {
            if (GameObjFunc.HasItems(g_sharedBuffer))
            {
                foreach (var buff in g_sharedBuffer.Values)
                {
                    ClearBuffer(buff);
                }
            }

            //g_sharedBuffer = null;
        }

        private static void ClearBuffer(GameObjBuffer buff)
        {
            if (buff != null && GameObjFunc.HasItems(buff.m_bufferList))
            {

                Debug.Log("Clear buff for "+buff.m_objPrefab.name);
                while (buff.m_bufferList.Count > 0)
                {
                    var obj = buff.m_bufferList.Pop();
                    if (obj != null)
                    {
                        Object.Destroy(obj);
                    }
                }
            }
        }

        internal GameObjBuffer(GameObject objPrefab, int defaultCount = 3)
        {
            ResetBuffer(objPrefab, defaultCount);
        }

        /// <summary>
        /// 设置buffer
        /// </summary>
        /// <param name="objPrefab">buffer中填充的物体</param>
        /// <param name="defaultCount">默认的长度：8</param>
        public void ResetBuffer(GameObject objPrefab, int defaultCount = 8)
        {
            ClearBuffer(this);

            m_bufferList = new Stack<GameObject>(defaultCount);
            m_historyCount = 0;
            this.m_objPrefab = objPrefab;

#if UNITY_EDITOR
            if (UnityEditor.EditorApplication.isPlaying == false) //编辑器没运行的时候，不能Expandbuffer
            {
                Debug.Log("Editor mode will not expand buffer");
                return;
            }

#endif
            ExpandBuffer(defaultCount);

        }

        /// <summary>
        /// 目前已经在buffer中的可用数量
        /// </summary>
        public int bufferSize
        {
            get
            {
                return m_bufferList.Count;
            }
        }

        /// <summary>
        /// 清除目前的buffer：
        /// </summary>
        public void ClearBuffer()
        {
            ClearBuffer(this);
        }

        /// <summary>
        /// 获得buffer中的obj（或生成一个），并放到对应的位置
        /// </summary>
        /// <remarks>buffer中物体不足1个时将会自我复制</remarks>
        public GameObject PopObjectBuffered(Vector3 position)
        {
            var obj = PopObjectBuffered();
            obj.transform.position = position;
            return obj;
        }

        /// <summary>
        /// 获得buffer中的obj（或生成一个）
        /// </summary>
        /// <remarks>buffer中物体不足1个时将会自我复制</remarks>
        public GameObject PopObjectBuffered()
        {
            GameObject result = m_bufferList.Pop();

            //--如果空了，先复制，再返回：
            if (m_bufferList.Count <= 0)
            {
                //再扩展buffer：
                ExpandBuffer();
            }

            //显示出来：
            result.SetActive(true);

            return result;
        }

        /// <summary>
        /// Get obj & Get component on this obj
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T PopObjectBuffered<T>() where T:MonoBehaviour
        {
            var obj = PopObjectBuffered();
            return obj.GetComponent<T>();
        }

        /// <summary>
        /// 将obj放回buffer
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="setParentToNull">是否要将父物体置空</param>
        public void PushObject(GameObject obj, bool setParentToNull = false)
        {
            obj.SetActive(false);
            if (setParentToNull)
            {
                obj.transform.SetParent(null);
            }

            m_bufferList.Push(obj);
        }

        /// <summary>
        /// 销毁物体（将obj放回buffer）
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <seealso cref="PushObject(GameObject)"/>
        public void DestroyObject(GameObject obj)
        {
            PushObject(obj);
        }

        /// <summary>
        /// 生成物体（从buffer中获得obj）
        /// </summary>
        /// <returns>The object.</returns>
        /// <seealso cref="PopObjectBuffered()"/>
        public GameObject InstantiateObj()
        {
            return PopObjectBuffered();
        }

        /// <summary>
        /// 将buffer扩展8位
        /// </summary>
        private void ExpandBuffer(int expandSize = 4)
        {
            for (int i = 0; i < expandSize; i++)
            {
                GameObject obj = Object.Instantiate(m_objPrefab);

#if UNITY_EDITOR
                obj.name = m_objPrefab.name + "_buffered_" + (m_historyCount++);//物体自身的序号用历史计数来识别
#endif
                m_bufferList.Push(obj);

                obj.SetActive(false);
            }


        }


    }
}